/*
 * metscrape - dumb scraper to pull down MetVUW weather images
 * Copyright (C) 2012  Jack Kelly <jack@jackkelly.name>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <curl/curl.h>
#include <libxml/HTMLparser.h>
#include <libxml/xpath.h>

#define FORECAST_URL ("http://www.metvuw.com/forecast/forecast1.php"     \
                      "?type=rain&region=seaussie&tim=06")
#define FORECAST_IMG_URL_PATTERN ("http://www.metvuw.com/forecast/%1$s/"    \
                                  "rain-seaussie-%1$s-%2$02d.gif")
#define FORECAST_IMG_FILE_PATTERN "rain-seaussie-%1$s-%2$03d.gif"
#define BUFSIZE 1024

/* Fetch options */
#define START 6
#define STEP 6
#define LAST 180

/* Fetch ALL THE IMAGES! */
static void fetch_images(const char* base) {
  puts("Fetching images...");

  char buf[BUFSIZE];
  CURL* curl = curl_easy_init();
  if (curl == NULL) {
    puts(" - Error");
    return;
  }

  size_t write_cb(void* p, size_t sz, size_t nmemb, void* ud) {
    FILE* fp = ud;
    size_t written = fwrite(p, sz, nmemb, fp);
    return sz * written;
  }
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);

  for (int i = START; i <= LAST; i += STEP) {
    snprintf(buf, BUFSIZE, FORECAST_IMG_URL_PATTERN, base, i);
    curl_easy_setopt(curl, CURLOPT_URL, buf);
    snprintf(buf, BUFSIZE, FORECAST_IMG_FILE_PATTERN, base, i);
    FILE* fp = fopen(buf, "w");
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    printf(" - %s...", buf);
    fflush(stdout);

    CURLcode status = curl_easy_perform(curl);
    fclose(fp);
    if (status == 0) {
      puts(" OK");
    } else {
      puts(" FAIL");
      unlink(buf);
    }
  }
  curl_easy_cleanup(curl);
}

/* Forecast URLs take the form
   http://www.metvuw.com/forecast/2012031712/rain-seaussie-2012031712-06.gif
   This fetches a sample page and finds the base (here 20120031712).
   Free the result with free().
   Return NULL if it gets confused. */
static char* get_forecast_base(void) {
  char* result = NULL;
  htmlParserCtxtPtr ctx = htmlNewParserCtxt();
  if (ctx == NULL) goto err0;
  htmlDocPtr doc = htmlCtxtReadFile(ctx,
                                    FORECAST_URL,
                                    NULL,
                                    (HTML_PARSE_RECOVER
                                     | XML_PARSE_NOERROR
                                     | XML_PARSE_NOWARNING));
  if (doc == NULL) goto err1;

  xmlXPathContextPtr xpath = xmlXPathNewContext(doc);
  if (xpath == NULL) goto err2;

  xmlXPathObjectPtr obj = xmlXPathEvalExpression((const xmlChar*)"//img", xpath);
  if (obj == NULL) goto err3;

  for (int i = 0; i < obj->nodesetval->nodeNr; i++) {
    xmlChar* prop = xmlGetProp(obj->nodesetval->nodeTab[i], (const xmlChar*)"src");
    if (prop != NULL) {
      if (strstr((const char*)prop, "rain-") != NULL) {
        free(result);
        result = strndup((const char*)prop + 2, 10);
      }
    }
    xmlFree(prop);
  }

  xmlFreeDoc(doc);
 err3:
  xmlXPathFreeObject(obj);
 err2:
  xmlXPathFreeContext(xpath);
 err1:
  xmlFreeParserCtxt(ctx);
 err0:
  return result;
}

int main(int argc, char *argv[]) {
  (void)argc;
  (void)argv;
  curl_global_init(CURL_GLOBAL_ALL);
  xmlInitParser();
  printf("Finding the forecast base... ");
  fflush(stdout);
  char* base = get_forecast_base();
  if (base != NULL) {
    printf("%s\n", base);
    fetch_images(base);
    free(base);
  } else {
    printf("error\n");
  }
  xmlCleanupParser();
  curl_global_cleanup();
  return 0;
}
